package com.bank.manage.service.Impl;

import com.alibaba.fastjson2.JSON;
import com.bank.manage.constance.BankConstance;
import com.bank.manage.convert.DtoConvert;
import com.bank.manage.enumerate.BankRedisKey;
import com.bank.manage.enumerate.BankStatus;
import com.bank.manage.enumerate.EventType;
import com.bank.manage.model.*;
import com.bank.manage.model.dto.RecordsDto;
import com.bank.manage.model.param.BankQueryParam;
import com.bank.manage.model.vo.RecordsVo;
import com.bank.manage.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.gavin.core.exception.ParamException;
import org.redis.service.RedisService;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

/**
 * RecordsServiceImpl
 *
 * @author grl
 * @date 2024/4/1
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RecordsServiceImpl implements RecordsService {


    private final BankOptCashRecordsService bankOptCashRecordsService;
    private final BankOptConsumeRecordsService bankOptConsumeRecordsService;
    private final BankOptIncomeRecordsService bankOptIncomeRecordsService;
    private final BankOptRechargeRecordsService bankOptRechargeRecordsService;
    private final BankOptSysRecordsService bankOptSysRecordsService;
    private final BankOptTransferRecordsService bankOptTransferRecordsService;
    private final BankCheckRecordsService bankCheckRecordsService;
    private final BankAccessWalletRecordService accessWalletRecordService;
    private final RedisService redisService;


    @Override
    public Boolean addRecord(RecordsDto dto) {
        String event = dto.getEventType();
        EventType eventType = EventType.valueOf(event);
        DtoConvert instances = DtoConvert.INSTANCES;
        LocalDateTime now = LocalDateTime.now();
        String dtoJson = JSON.toJSONString(dto);
        return switch (eventType) {
            case CASH -> {
                BankOptCashRecords bankOptCashRecords = instances.recordsDtoToOptCash(dto);
                bankOptCashRecords.setCreateTime(now);
                bankOptCashRecords.setRequestJson(dtoJson);
                yield bankOptCashRecordsService.save(bankOptCashRecords);
            }
            case CONSUME -> {
                BankOptConsumeRecords bankOptConsumeRecords = instances.recordsDtoToOptConsume(dto);
                bankOptConsumeRecords.setCreateTime(now);
                bankOptConsumeRecords.setRequestJson(dtoJson);
                yield bankOptConsumeRecordsService.save(bankOptConsumeRecords);
            }
            case INCOME -> {
                BankOptIncomeRecords bankOptIncomeRecords = instances.recordsDtoToOptIncome(dto);
                bankOptIncomeRecords.setCreateTime(now);
                bankOptIncomeRecords.setRequestJson(dtoJson);
                yield bankOptIncomeRecordsService.save(bankOptIncomeRecords);
            }
            case RECHARGE -> {
                BankOptRechargeRecords bankOptRechargeRecords = instances.recordsDtoToOptRecharge(dto);
                bankOptRechargeRecords.setCreateTime(now);
                bankOptRechargeRecords.setRequestJson(dtoJson);
                yield bankOptRechargeRecordsService.save(bankOptRechargeRecords);

            }
            case SYS -> {
                BankOptSysRecords bankOptSysRecords = instances.recordsDtoToOptSys(dto);
                bankOptSysRecords.setCreateTime(now);
                bankOptSysRecords.setRequestJson(dtoJson);
                yield bankOptSysRecordsService.save(bankOptSysRecords);
            }
            case TRANSFER -> {
                BankOptTransferRecords bankOptTransferRecords = instances.recordsDtoToOptTransfer(dto);
                bankOptTransferRecords.setCreateTime(now);
                bankOptTransferRecords.setRequestJson(dtoJson);
                yield bankOptTransferRecordsService.save(bankOptTransferRecords);
            }
        };
    }

    @Override
    public RecordsVo recordQuery(BankQueryParam param) {
        checkParam(param);
        String event = param.getEventType();
        EventType eventType = EventType.valueOf(event);
        String desc = BankConstance.IDENTIFICATION_SUCCESS;
        boolean walletRecord = false;
        // 查询 bank_access_wallet_record
        BankAccessWalletRecord bankAccessWalletRecord = getBankAccessWalletRecord(eventType,param);
        if (bankAccessWalletRecord != null) {
            walletRecord = true;
        } else {
            desc = BankConstance.IDENTIFICATION_NOT_FIND_USER_FAIL;
        }
        Boolean recordFlag = false;
        if (walletRecord) {
            recordFlag = checkRecordFlag(param, eventType);
            if (!recordFlag) {
                desc = BankConstance.IDENTIFICATION_NOT_FIND_FAIL;
            }
        }
        boolean redisRecord = false;
        if (walletRecord && recordFlag) {
            // 校验时效
            redisRecord = checkRedisValue(param, bankAccessWalletRecord);
            if (!redisRecord) {
                desc = BankConstance.IDENTIFICATION_TIMEOUT_FAIL;
            }
        }
        RecordsVo vo = new RecordsVo();
        vo.setResult(walletRecord && recordFlag && redisRecord);
        vo.setDesc(desc);
        // 增加查询记录
        bankCheckRecordsService.addCheckRecord(vo, param);
        return vo;
    }

    /**
     * 参数校验
     * @param param
     */
    private void checkParam(BankQueryParam param) {
        String eventType = param.getEventType();
        if(!EventType.SYS.equals(EventType.valueOf(eventType))){
            if(StringUtils.isBlank(param.getWalletNum())){
                log.error("非系统相关的查询必须携带钱包号 {}",JSON.toJSONString(param));
                throw ParamException.le(BankStatus.WALLET_NUM_PARAM_EMPTY);
            }
        }
    }

    /**
     * 获取 用户信息
     * @param param
     * @param eventType
     * @return
     */
    private BankAccessWalletRecord getBankAccessWalletRecord(EventType eventType,BankQueryParam param) {
        BankAccessWalletRecord bankAccessWalletRecord;
        if(!EventType.SYS.equals(eventType)){
            bankAccessWalletRecord = accessWalletRecordService.queryUserNum(param);
        }else {
            bankAccessWalletRecord = accessWalletRecordService.queryUserNum(param.getRequestId());
        }
        return bankAccessWalletRecord;
    }

    /**
     * 查询redis 中是否存在改值
     * @param param
     * @param bankAccessWalletRecord
     * @return
     */
    private boolean checkRedisValue(BankQueryParam param, BankAccessWalletRecord bankAccessWalletRecord) {
        String userNum = bankAccessWalletRecord.getUserNum();
        BankRedisKey redisKey = BankRedisKey.WALLET_VERIFICATION_SPECIFIC.joinKey(userNum, param.getEventType(), param.getSpecificValue());
        return StringUtils.isNotBlank(redisService.get(redisKey));
    }

    /**
     * 检查是否通过验证
     *
     * @param param
     * @param eventType
     * @return
     */
    @Override
    public Boolean checkRecordFlag(BankQueryParam param, EventType eventType) {
        return switch (eventType) {
            case CASH -> {
                yield bankOptCashRecordsService.recordExistQuery(param);
            }
            case CONSUME -> {
                yield bankOptConsumeRecordsService.recordExistQuery(param);
            }
            case INCOME -> {
                yield bankOptIncomeRecordsService.recordExistQuery(param);
            }
            case RECHARGE -> {
                yield bankOptRechargeRecordsService.recordExistQuery(param);
            }
            case SYS -> {
                yield bankOptSysRecordsService.recordExistQuery(param);
            }
            case TRANSFER -> {
                yield bankOptTransferRecordsService.recordExistQuery(param);
            }
        };
    }
}